
// --------------- Global variables ---------------

matrix gWorldViewProj;
matrix gViewProj;
float4 gCameraPosition;
float gTime;

// Fog
bool	gFogEnabled = false;
float3	gFogColor;
float	gFogStart;
float	gFogEnd;

// Primitives
float4 gPrimitiveColor;

// HeightMap
texture gEnvMap;

// Mesh
texture gMeshTexture;


// ------------------- Samplers -------------------

// HeightMap
sampler EnvMapSampler = sampler_state
{
	Texture = <gEnvMap>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};

// Mesh
sampler MeshTextureSampler = sampler_state
{
	Texture = <gMeshTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};


// ----------------- Vertex Shader ----------------

struct VS_INPUT
{
	float3 Position		: POSITION;
	float3 Normal		: NORMAL;
	float3 Tangent		: TANGENT;
	float3 Binormal		: BINORMAL;
	float2 TextureUV	: TEXCOORD0;
};

struct VS_INPUT_INSTANCE
{
	// Stream Source 1
	float3 Position				: POSITION;
	float3 Normal				: NORMAL;
	float3 Tangent				: TANGENT;
	float3 Binormal				: BINORMAL;
	float2 TextureUV			: TEXCOORD0;

	// Stream Source 2
	float4 InstanceMatrixRow1	: TEXCOORD1;
	float4 InstanceMatrixRow2	: TEXCOORD2;
	float4 InstanceMatrixRow3	: TEXCOORD3;
	float4 InstanceMatrixRow4	: TEXCOORD4;
};

struct VS_INPUT_BILLBOARD
{
	float3 Position			: POSITION;
	float3 PositionWorld	: TEXCOORD0;
	float2 TextureUV		: TEXCOORD1;
	float2 WindData			: TEXCOORD2;		// x : Wind amplitude | y : Wind Offset
};

struct VS_INPUT_IMPOSTOR
{
	float3 Position			: POSITION;
	float3 PositionWorld	: TEXCOORD0;
	float2 TextureUV		: TEXCOORD1;
	float Rotation			: TEXCOORD2;		// Original rotation of the object around the Y axis (in degree)
};

struct VS_OUTPUT
{
	float4 Position		: POSITION;
	float2 TextureUV	: TEXCOORD0;
	float4 Color		: COLOR;
	float FogLerpValue	: TEXCOORD1;
};

struct VS_OUTPUT_SKYDOME
{
	float4 Position		: POSITION;
	float3 EnvTexture	: TEXCOORD0;
	float FogLerpValue	: TEXCOORD1;
};


// ------------------ Techniques ------------------


// ---------------------------------------------
//					Primitive
// ---------------------------------------------

VS_OUTPUT PrimitiveVS(VS_INPUT input)
{
	VS_OUTPUT output;
	output.Position = mul(float4(input.Position, 1.0f), gWorldViewProj);
	output.Color = gPrimitiveColor;
	output.TextureUV = input.TextureUV;
	
	// Fog
	if (gFogEnabled)
	{
		float dist = distance(input.Position, gCameraPosition);
		output.FogLerpValue = saturate((dist - gFogStart) / gFogEnd);
	}

	return output;
}

VS_OUTPUT InstancedPrimitiveVS(VS_INPUT_INSTANCE input)
{
	VS_OUTPUT output;
	
	matrix instanceMatrix = matrix(input.InstanceMatrixRow1, input.InstanceMatrixRow2, input.InstanceMatrixRow3, input.InstanceMatrixRow4);
	output.Position = mul(float4(input.Position, 1.0f), instanceMatrix * gViewProj);

	output.Color = gPrimitiveColor;
	output.TextureUV = input.TextureUV;
	
	// Fog
	
	if (gFogEnabled)
	{
		float dist = distance(input.Position, gCameraPosition);
		output.FogLerpValue = saturate((dist - gFogStart) / gFogEnd);
	}

	return output;
}

float4 PrimitivePS(VS_OUTPUT input) : COLOR
{
	float3 color = input.Color.xyz;

	// Fog
	if (gFogEnabled)
		color = lerp(color, gFogColor, input.FogLerpValue);

	return float4(color, 1.0f);
}

technique Primitive
{
	pass p0
	{
		VertexShader = compile vs_3_0 PrimitiveVS();
		PixelShader = compile ps_3_0 PrimitivePS();
	}
}

technique InstancedPrimitive
{
	pass p0
	{
		VertexShader = compile vs_3_0 InstancedPrimitiveVS();
		PixelShader = compile ps_3_0 PrimitivePS();
	}
}


// ---------------------------------------------
//					SkyDome
// ---------------------------------------------

VS_OUTPUT_SKYDOME SkyDomeVS(VS_INPUT input)
{
	VS_OUTPUT_SKYDOME output;
	output.Position = mul(float4(input.Position, 1.0f), gWorldViewProj).xyww;
	output.EnvTexture = input.Position;

	if (gFogEnabled)
	{
		output.FogLerpValue = saturate(1.0f - input.Position.y);
	}

	return output;
}

float4 SkyDomePS(VS_OUTPUT_SKYDOME input) : COLOR
{
	float4 color = texCUBE(EnvMapSampler, input.EnvTexture);
	if (gFogEnabled)
	{
		return float4(lerp(color.rgb, gFogColor, input.FogLerpValue), 1.0f);
	}
	return color;
}

technique SkyDome
{
	pass p0
	{
		VertexShader = compile vs_3_0 SkyDomeVS();
		PixelShader = compile ps_3_0 SkyDomePS();
		
		CullMode = None;		// We are inside the sphere, we need to disabled culling
	}
}


// ---------------------------------------------
//					HeightMap
// ---------------------------------------------

VS_OUTPUT HeightMapVS(VS_INPUT input)
{
	VS_OUTPUT output;
	output.Position = mul(float4(input.Position, 1.0f), gWorldViewProj);
	output.Color = float4(input.Normal, 1.0f);
	output.TextureUV = input.TextureUV;
	
	// Fog
	
	if (gFogEnabled)
	{
		float dist = distance(input.Position, gCameraPosition);
		output.FogLerpValue = saturate((dist - gFogStart) / gFogEnd);
	}

	return output;
}

float4 HeightMapPS(VS_OUTPUT input) : COLOR
{
	float3 color = input.Color.xyz;

	// Fog
	if (gFogEnabled)
		color = lerp(color, gFogColor, input.FogLerpValue);

	return float4(color, 1.0f);
}

technique HeightMap
{
	pass p0
	{
		VertexShader = compile vs_3_0 HeightMapVS();
		PixelShader = compile ps_3_0 HeightMapPS();
	}
}


// ---------------------------------------------
//						Mesh
// ---------------------------------------------

VS_OUTPUT MeshVS(VS_INPUT input)
{
	VS_OUTPUT output;
	output.Position = mul(float4(input.Position, 1.0f), gWorldViewProj);
	output.Color = float4(1.0f, 1.0f, 1.0f, 1.0f);
	output.TextureUV = input.TextureUV;
	
	// Fog
	if (gFogEnabled)
	{
		float dist = distance(output.Position, gCameraPosition);
		output.FogLerpValue = saturate((dist - gFogStart) / gFogEnd);
	}

	return output;
}

float4 MeshPS(VS_OUTPUT input) : COLOR
{
	// Texture
	float4 color = tex2D(MeshTextureSampler, input.TextureUV);
	
	// Fog
	//if (gFogEnabled)
	//	color = lerp(color.rgb, gFogColor, input.FogLerpValue);

	return color;
}

technique Mesh
{
	pass p0
	{
		VertexShader = compile vs_3_0 MeshVS();
		PixelShader = compile ps_3_0 MeshPS();
		
		AlphaRef = 150;
		AlphaFunc = GreaterEqual;
		AlphaTestEnable = true;
	}
}


// ---------------------------------------------
//					Billboard
// ---------------------------------------------

VS_OUTPUT BillboardVS(VS_INPUT_BILLBOARD input)
{
	VS_OUTPUT output = (VS_OUTPUT)0;
	
	// Get the direction of the billboard
	float3 look = gCameraPosition - input.PositionWorld;
	look.y = 0;
	look = normalize(look);
	float3 up = float3(0, 1.0f, 0);
	float3 right = cross(up, look);
	
	// Create the matrix facing the camera
	float4x4 lookAtMatrix;
	lookAtMatrix[0] = float4(right, 0);
	lookAtMatrix[1] = float4(up, 0);
	lookAtMatrix[2] = float4(look, 0);
	lookAtMatrix[3] = float4(0, 0, 0, 1.0f);

	float4 worldPos = mul(float4(input.Position, 1.0f), lookAtMatrix);
	worldPos = worldPos + float4(input.PositionWorld, 0);

	// Apply the wind force
	if (input.WindData.x != 0.0f)
		worldPos.xyz += float(input.WindData.x * sin(gTime + input.WindData.y)) * right;

	output.Position = mul(worldPos, gViewProj);

	output.TextureUV = input.TextureUV;

	return output;
}

float4 BillboardPS(VS_OUTPUT input) : COLOR
{
	float4 color = tex2D(MeshTextureSampler, input.TextureUV);

	return color;
}

technique Billboard
{
	pass p0
	{
		VertexShader = compile vs_3_0 BillboardVS();
		PixelShader = compile ps_3_0 BillboardPS();

		CullMode = None;

		AlphaRef = 150;
		AlphaFunc = GreaterEqual;
		AlphaTestEnable = true;
	}
}


// ---------------------------------------------
//					Impostor
// ---------------------------------------------

VS_OUTPUT ImpostorVS(VS_INPUT_BILLBOARD input)
{
	VS_OUTPUT output = (VS_OUTPUT)0;
	
	// Get the direction of the billboard
	float3 look = gCameraPosition - input.PositionWorld;
	look.y = 0;
	look = normalize(look);
	float3 up = float3(0, 1.0f, 0);
	float3 right = cross(up, look);
	
	// Create the matrix facing the camera
	float4x4 lookAtMatrix;
	lookAtMatrix[0] = float4(right, 0);
	lookAtMatrix[1] = float4(up, 0);
	lookAtMatrix[2] = float4(look, 0);
	lookAtMatrix[3] = float4(0, 0, 0, 1.0f);

	float4 worldPos = mul(float4(input.Position, 1.0f), lookAtMatrix);
	worldPos = worldPos + float4(input.PositionWorld, 0);

	output.Position = mul(worldPos, gViewProj);

	output.TextureUV = input.TextureUV;

	return output;
}

float4 ImpostorPS(VS_OUTPUT input) : COLOR
{
	float4 color = tex2D(MeshTextureSampler, input.TextureUV);

	return color;
}

technique Impostor
{
	pass p0
	{
		VertexShader = compile vs_3_0 ImpostorVS();
		PixelShader = compile ps_3_0 ImpostorPS();

		CullMode = None;

		AlphaRef = 150;
		AlphaFunc = GreaterEqual;
		AlphaTestEnable = true;
	}
}
